package com.aydx.minirpc.core.spring;

import com.aydx.minirpc.MiniRpcConfiguretionImpl;
import com.aydx.minirpc.core.MiniRpcConfig;
import com.aydx.minirpc.core.MiniRpcContext;
import com.aydx.minirpc.core.MiniRpcException;
import com.aydx.minirpc.core.common.NameUtils;
import com.aydx.minirpc.core.common.StringTools;
import com.aydx.minirpc.core.proxy.*;
import com.aydx.minirpc.core.register.*;
import com.aydx.minirpc.core.serializable.SerializableFactory;
import com.aydx.minirpc.core.server.AnswerRemoteCall;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

import java.io.IOException;
import java.net.URISyntaxException;


/**
 * Created by aydx on 2019/1/6.
 * 启动器
 */
public abstract class MiniRpcSpringBootConfigurtion implements BeanDefinitionRegistryPostProcessor, ApplicationContextAware {

    private Logger logger = LoggerFactory.getLogger(MiniRpcConfiguretionImpl.class);
    private ApplicationContext context;

    public abstract String getPackageScan();

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
    }
    public abstract BeanProxyFactory getBeanProxyFactory();
    public abstract AnswerRemoteCall getAnswerRemoteCall();
    public abstract SerializableFactory getSerializableFactory();
    public abstract MiniRpcConfig getMiniRpcConfig();
    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {
        try{
            //构建rpcContext
            MiniRpcContext rpcContext=MiniRpcContext.newInstance(getMiniRpcConfig(),getBeanProxyFactory(),getAnswerRemoteCall(),getSerializableFactory());
            //注册到spring
            registerRpcContext(beanDefinitionRegistry,rpcContext);
            //启动netty Server
            rpcContext.startServer();
            //扫描、加载、注册@RemoteCall和@RemoteServer注解
            loadRemoteCall(beanDefinitionRegistry,rpcContext);
            //
        }catch (Exception e){
            logger.error("构建MiniRpc上下文失败",e);
            throw new MiniRpcException("构建MiniRpc上下文失败",e);
        }
    }


    //将rpcContext注册到spring中
    private void registerRpcContext(BeanDefinitionRegistry beanDefinitionRegistry, MiniRpcContext rpcContext){
        String beanName=NameUtils.toLowerCaseFirstOne(MiniRpcContext.class.getSimpleName());
        BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MiniRpcContext.class);
        GenericBeanDefinition definition = (GenericBeanDefinition) builder.getRawBeanDefinition();
        definition.getPropertyValues().add("rpcContext",  rpcContext);
        definition.setBeanClass(RpcContextFactory.class);
        definition.setAutowireMode(GenericBeanDefinition.AUTOWIRE_BY_TYPE);
        definition.setScope("singleton");
        beanDefinitionRegistry.registerBeanDefinition(beanName, definition);
    }
    //将interface注册到spring中
    private void registerInterface(BeanDefinitionRegistry beanDefinitionRegistry,MiniRpcContext rpcContext, Class<?> klass){
        String beanName=NameUtils.toLowerCaseFirstOne(klass.getSimpleName());
        BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(klass);
        GenericBeanDefinition definition = (GenericBeanDefinition) builder.getRawBeanDefinition();
        definition.getPropertyValues().add("interfaceClass", definition.getBeanClassName());
        definition.getPropertyValues().add("proxy",  rpcContext.getBeanProxyFactory().createProxy(klass));
        definition.setBeanClass(ProxyBeanFactory.class);
        definition.setAutowireMode(GenericBeanDefinition.AUTOWIRE_BY_TYPE);
        definition.setScope("singleton");
        beanDefinitionRegistry.registerBeanDefinition(beanName, definition);
    }
    //扫描包，并加载注解
    private void loadRemoteCall(BeanDefinitionRegistry beanDefinitionRegistry,MiniRpcContext rpcContext) throws IOException, URISyntaxException {
        String packageScanRoot=getPackageScan();
        if(StringTools.isEmpty(packageScanRoot)){
            throw new MiniRpcException("缺少扫描路径配置");
        }
        PackageScanner packageScanner=new PackageScanner() {
            @Override
            public <T> T dealClass(Class<T> klass) {
                //检查remotecall注解
                RemoteCall remoteCallAnnotation=klass.getAnnotation(RemoteCall.class);
                if(remoteCallAnnotation!=null){
                    /**
                     * 生成代理并注册到bean
                     */
                    registerInterface(beanDefinitionRegistry,rpcContext,klass);
                }
                //检查remoteService注解
                RemoteService serviceAnnotation=klass.getAnnotation(RemoteService.class);
                if(serviceAnnotation!=null){
                    T t=context.getBean(klass);
                    if(t==null){
                        throw new MiniRpcException("找不到该Bean"+klass.getName());
                    }
                    //注册节点
                    String group= serviceAnnotation.group();
                    String version=serviceAnnotation.version();
                    Class[] interfaceClassList=klass.getInterfaces();
                    if(interfaceClassList==null || interfaceClassList.length==0){
                        throw new MiniRpcException("服务类必须实现接口"+klass.getName());
                    }
                    String register=serviceAnnotation.register();
                    try {
                        RegisterCenter registerCenter=rpcContext.getRegisterCenter();
                        for(Class interfaceClass:interfaceClassList){
                            String interfaceName=interfaceClass.getName();
                            //在spring中创建一个别名，用来识别是remotecall还是remoteserver
                            String[] nameList= context.getBeanNamesForType(klass);
                            if(nameList==null || nameList.length==0){
                                throw new MiniRpcException("没有实现类："+klass.getName());
                            }
                            beanDefinitionRegistry.registerAlias(nameList[0],"provider_"+interfaceName);
                            NodeInfo nodeInfo=new NodeInfo().setNodeType(NodeType.PROVIDER).setInterfaceName(interfaceName).setGroup(group).setVersion(version).setIp(rpcContext.getMiniRpcConfig().getSelfServiceIp()).setPort(rpcContext.getMiniRpcConfig().getSelfServicePort());
                            registerCenter.register(register,nodeInfo);
                        }
                    } catch (Exception e) {
                        logger.error("注册节点时发生异常",e);
                        throw new MiniRpcException("注册provider节点时发生异常",e);
                    }
                }
                return null;
            }
        };
        logger.info("开始扫描注册minirpc,路径为:{}",getPackageScan());
        packageScanner.packageScan(packageScanRoot);
    }
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.context=applicationContext;
    }

    public ApplicationContext getContext() {
        return context;
    }

}
